// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using SnapObjects.Expressions;

namespace SnapObjects.Data
{
    internal class TemplateModelExportProvider : ITemplateExportProvider
    {
        private readonly IEnumerable<object> _models;
        private readonly IDefaultExpressionEval _evaluater;
        private readonly ITemplateDataFormater _formater;
        private readonly ITemplateExportMetadataProvider _metadataProvider;

        public TemplateModelExportProvider(
            IEnumerable<object> models,
            DataFormat dataFormat = DataFormat.Json,
            ITemplateExportMetadataProvider metadataProvider = null)
        {
            var modelType = models?.GetType();

            if (modelType != null)
            {
                if (modelType.IsGenericType)
                {
                    modelType = modelType.GetGenericArguments()[0];
                }
                else if (modelType.IsArray)
                {
                    modelType = modelType.GetElementType();
                }
            }

            _models = models;
            _metadataProvider = metadataProvider;

            _formater = TemplateDataFormaterFactory.GetFormater(dataFormat);
            _evaluater = DefaultExpressionEvalFactory.Current.GetExpressionEval(modelType);
        }

        public object Evaluate(string expression, object model)
        {
            var options = new EvaluateParserOptions(expression, SourceKind.Default, model);

            if (_metadataProvider != null)
            {
                options.Metadatas.AddMetadataProvider(_metadataProvider);
            }

            var value = _evaluater.Evaluate(options);

            value = _formater.Format(value);

            return value;
        }

        public string Partial(string name, object model)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<object> GetDataSource()
        {
            return _models;
        }

        public IEnumerable<IGrouping<object, object>> GroupBy(
            IEnumerable<object> source, string selector, string order = null)
        {
            var conditions = selector.Split(new char[] { ',' },
                StringSplitOptions.RemoveEmptyEntries);

            var groupDel = this.GetGroupByDel(conditions);

            var groups = source.GroupBy(x => groupDel.Invoke(_models, x), x => x);

            var retGroups = new List<IGrouping<object, object>>();

            foreach (var group in groups)
            {
                var item = new Grouping<object, object>(
                    group.Key, this.OrderBy(group, order));

                retGroups.Add(item);
            }

            return retGroups;
        }

        public IEnumerable<object> OrderBy(IEnumerable<object> source, string format)
        {
            IOrderedEnumerable<object> orderable = null;

            var sorts = this.GetSortDel(format);

            foreach (var item in sorts)
            {
                if (orderable == null)
                {
                    orderable = item.Value.IsAscending
                        ? source.OrderBy(m => item.Key(_models, m)?.ToString(),
                            item.Value.StringComparer)
                        : source.OrderByDescending(m => item.Key(_models, m)?.ToString(),
                            item.Value.StringComparer);
                }
                else
                {
                    orderable = item.Value.IsAscending
                        ? orderable.ThenBy(m => item.Key(_models, m)?.ToString(),
                            item.Value.StringComparer)
                        : orderable.ThenByDescending(m => item.Key(_models, m)?.ToString(),
                            item.Value.StringComparer);
                }
            }

            return orderable ?? source;
        }

        public object GetDefaultModel()
        {
            return _models.FirstOrDefault();
        }

        private IDictionary<LambdaDelegate, DefaultSortOptions> GetSortDel(string sortStr)
        {
            if (!String.IsNullOrWhiteSpace(sortStr))
            {
                var parser = new DefaultSortParser(_evaluater);

                return parser.Parse(sortStr);
            }

            return new Dictionary<LambdaDelegate, DefaultSortOptions>();
        }

        private LambdaDelegate GetGroupByDel(IEnumerable<string> selector)
        {
            var strExpr = selector.GetNewStr();

            return _evaluater.Compile(strExpr);
        }
    }
}
